import portalocker
import re
import requests
import uiautomator2 as u2
import time, sys, os,io
import json
from lxml import etree
from PIL import Image
from loguru import logger
import pytesseract
import traceback
import subprocess
from PIL import ImageOps
import aircv as ac
import pyttsx3
import random
import datetime
import sqlite3
from multiprocessing import Lock,Value,Process, Manager,Queue, freeze_support
import tkinter as tk
from tkinter import ttk
import winsound
import msvcrt
import pymysql

# 设置Tesseract OCR路径
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
#华为镜像源：https://repo.huaweicloud.com/repository/pypi/simple/
# python312 -m pip install -r requirements.txt -i https://repo.huaweicloud.com/repository/pypi/simple/
# 模拟器分辨率540*960 （赛期app）
#7f0860c2a29e619134ee30f8b5e45db55bba6b07
# www.ey900.com:3306
#python setup.py build_ext --inplace
process_info_list =[]  # 进程列表

class TradingManagerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("自动监控系统")
        self.root.geometry("1320x500")
        
        # 初始化数据
        self.hsi_url=''


        #self.accounts = self.generate_sample_data(20)
        self.current_index = 0  # 初始指数值
        self.base_index = 0     # 基准指数
        self.get_server_info()
        self.setup_ui()
        self.refresh_data()
        self.update_clock()  # 启动时钟更新
    
    
    
    def setup_ui(self):
        # 主容器
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # ========== Treeview部分 ==========
        tree_container = ttk.Frame(main_frame)
        tree_container.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
        
        # 定义列
        columns = (
            'row_num', 'account_id', 'account_type', 'name', 
            'simulator_id', 'simulator_name', 'status', 
            'balance', 'floating_pl', 
            'closed_pl', 'fee', 'round_times', 'update_time'
        )
        
        # 列标题映射
        column_headers = {
            'row_num': '行编号',
            'account_id': '账号',
            'account_type': '类型',
            'name': '姓名',
            'simulator_id': '模拟器ID',
            'simulator_name': '模拟器名称',
            'status': '状态',
            'balance': '账户金额',
            'floating_pl': '浮动盈亏',
            'closed_pl': '平仓盈亏',
            'fee': '手续费',
            'round_times': '轮/次',
            'update_time': '更新时间'
        }
        
        # 创建Treeview
        self.tree = ttk.Treeview(
            tree_container, 
            columns=columns, 
            show='headings',
            selectmode='browse'
        )
        
        # 配置列
        column_widths = {
            'row_num': 50,
            'account_id': 200,
            'account_type': 60,
            'name': 80,
            'simulator_id': 130,
            'simulator_name': 100,
            'status': 70,
            'balance': 90,
            'floating_pl': 90,
            'closed_pl': 90,
            'fee': 80,
            'round_times': 100,
            'update_time': 150
        }
        
        column_anchors = {
            'row_num': 'center',
            'account_id': 'center',
            'account_type': 'center',
            'name': 'center',
            'simulator_id': 'center',
            'simulator_name': 'center',
            'status': 'center',
            'balance': 'e',
            'floating_pl': 'e',
            'closed_pl': 'e',
            'fee': 'e',
            'round_times': 'center',
            'update_time': 'center'
        }
        
        for col in columns:
            self.tree.heading(col, text=column_headers[col])
            self.tree.column(col, 
                            width=column_widths[col], 
                            anchor=column_anchors.get(col, 'w'),
                            stretch=False)
        
        # ========== 样式配置 ==========
        self.style = ttk.Style()
        
        # 全局Treeview样式
        self.style.configure('Treeview', 
                           rowheight=28,
                           font=('Microsoft YaHei', 10),
                           borderwidth=1,
                           relief='solid')
        
        # 选中项样式
        self.style.map('Treeview',
            background=[('selected', '#347083')],
            foreground=[('selected', 'white')]
        )
        
        # 隔行变色配置
        self.tree.tag_configure('oddrow', background='#f5f5f5')
        self.tree.tag_configure('evenrow', background='white')
        

         # 获取adb实际连接设备列表,并插入表格
        try:
            db=sqlite3.connect(r"db.sqlite3")
            cursor=db.cursor()
            # 匹配之前需要先清空用户表
            cursor.execute("delete from user")
            db.commit()
            result = subprocess.run(['adb', 'devices'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            n=0
            for line in result.stdout.splitlines():
                if 'List of devices' not in line and line!="":
                    print(line.split('\t'))
                    device_id=line.split('\t')[0] # 截取设备号：emulator-5556  
                    port=device_id.split('-')[1]  # 截取端口号  (端口号计算规则：5554+（index*2）)
                    index=(int(port)-5554)/2
                    # 获取模拟器对应的窗口名称
                    result = subprocess.run(['ldconsole', 'list3','--index',str(index)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
                    device_name=''
                    for line in result.stdout.splitlines():
                        value=line.split('\t')[0]
                        dv=value.split(',')
                        print(dv)
                        device_name=dv[1].strip()
                        break
                    


                    # 先添加界面内容，将行id和模拟器id对应上
                    # ('行编号','账号','类型', '姓名','模拟器ID','模拟器名称','状态','账户金额','浮动盈亏','平仓盈亏','手续费','轮/次','更新时间')
                    row_tag = 'oddrow' if n % 2 == 1 else 'evenrow'
                    rowid=self.tree.insert('','end',values=('','','','',device_id,device_name,'已启动',0,0,0,0,'-/-','-'),tags=(row_tag,))
                    self.tree.set(rowid,'row_num',rowid )  # 将行id更新为实际获取的行id
                    cursor.execute("INSERT INTO user(row_id,device_index,device_id,device_name,device_status,addtime,edittime) VALUES (?,?,?,?,?,?,?)",(rowid,index,device_id,device_name,'已启动', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
                    db.commit()
                    #启动模拟器进程
                    p = Process(target= DevicePlayer(device_id,device_name,rowid).connect_device, args=(),name=f'{device_id}')
                    p.daemon = True
                    p.start()
                    process_info_list.append({
                        'pid': p.pid,
                        'device_id': device_id,
                        'device_name': device_name,
                        'process': p

                    })
                    
                    n=n+1
                    time.sleep(2)
        
        except:
            logger.error(traceback.format_exc())
        '''
        # 添加数据
        for i, account in enumerate(self.accounts):
            # 隔行变色
            row_tag = 'oddrow' if i % 2 == 1 else 'evenrow'
            
            # 插入数据
            self.tree.insert(
                '', 
                tk.END, 
                values=(
                    account['row_num'],
                    account['account_id'],
                    account['account_type'],
                    account['name'],
                    account['simulator_id'],
                    account['simulator_name'],
                    account['status'],
                    f"{account['balance']:,.2f}",
                    f"{account['trade_index']:.3f}",
                    f"{account['floating_pl']:+,.2f}",
                    f"{account['closed_pl']:+,.2f}",
                    f"{account['fee']:,.2f}",
                    account['round_times'],
                    account['update_time']
                ),
                tags=(row_tag,)
            )
        '''
        # 滚动条
        vsb = ttk.Scrollbar(tree_container, orient=tk.VERTICAL, command=self.tree.yview)
        hsb = ttk.Scrollbar(tree_container, orient=tk.HORIZONTAL, command=self.tree.xview)
        self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
        
        # 使用grid布局
        self.tree.grid(row=0, column=0, sticky=tk.NSEW)
        vsb.grid(row=0, column=1, sticky=tk.NS)
        hsb.grid(row=1, column=0, sticky=tk.EW)
        
        # 配置tree_container的grid权重
        tree_container.grid_rowconfigure(0, weight=1)
        tree_container.grid_columnconfigure(0, weight=1)
        
        # ========== 按钮控制部分 ==========
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=(5, 0))
        
        # 操作按钮
        self.start_btn = ttk.Button(
            button_frame, 
            text="启动程序", 
            command=self.start_account,
            state=tk.DISABLED
        )
        
        self.stop_btn = ttk.Button(
            button_frame, 
            text="停止程序", 
            command=self.stop_account,
            state=tk.DISABLED
        )
        '''
        refresh_btn = ttk.Button(
            button_frame, 
            text="刷新数据", 
            command=self.refresh_data
        )
        refresh_btn.pack(side=tk.RIGHT, padx=5)
        '''

       
        
        # 时间显示
        time_frame = ttk.Frame(button_frame)
        time_frame.pack(side=tk.LEFT, padx=10)
        
        #ttk.Label(time_frame, text="系统时间:", font=('Microsoft YaHei', 10)).pack(anchor='w')
        
        self.time_label = tk.Label(
            time_frame,
            text="00:00:00",
            font=('Courier New', 24, 'bold'),
            fg="#00FF00",
            bg="black",
            padx=10,
            pady=2
        )
        self.time_label.pack()
        
        # 指数显示
        index_frame = ttk.Frame(button_frame)
        index_frame.pack(side=tk.LEFT, padx=10)
        
        #ttk.Label(index_frame, text="沪深300:", font=('Microsoft YaHei', 10)).pack(anchor='w')
        
        self.index_label = tk.Label(
            index_frame,
            text="3,250.42",
            font=('Courier New', 24, 'bold'),
            bg="black",
            padx=10,
            pady=2
        )
        self.index_label.pack()
        # ========== 状态栏 ==========
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = ttk.Label(
            main_frame, 
            textvariable=self.status_var, 
            relief=tk.SUNKEN,
            padding=(5, 2),
            anchor=tk.W
        )
        status_bar.pack(fill=tk.X, pady=(5, 0))
        
        # ========== 绑定事件 ==========
        self.tree.bind('<<TreeviewSelect>>', self.update_button_state)
        
        # 初始禁用按钮
        self.update_button_state()
    def update_clock(self):
        """更新时间显示"""
        current_time = f'时间 {time.strftime("%H:%M:%S")}'
        self.time_label.config(text=current_time)
        #print('更新时间指数,hsi_url',self.hsi_url)
        if self.hsi_url=='' or self.hsi_url is None:
            pass
        try:
            if self.base_index==0:
                hsi_name,self.base_index=self.update_hsi()
            else:
                #print('self.base_index=',self.base_index)
                hsi_name,self.current_index = self.update_hsi()
                change = self.current_index - self.base_index
                change_percent = (change / self.base_index) * 100
                
                # 格式化显示
                index_text = f"{self.current_index:,.2f}"
                change_text = f"{change:+.2f} ({change_percent:+.2f}%)"
                
                # 设置颜色
                if change > 0:
                    color = "#FF0000"   # 上涨红色
                    change_text = "↑ " + change_text
                elif change < 0:
                    color = "#00FF00"  # 下跌绿色
                    change_text = "↓ " + change_text
                elif change == 0:
                    color = "#F0F5F0"  # 白色
                    change_text =  change_text
                
                self.index_label.config(
                    text=f"{hsi_name} {index_text} [{change_text}]",
                    fg=color
                )
        except:
            logger.error(traceback.format_exc())
                
            # 每秒更新一次
        self.root.after(1000, self.update_clock)


    def get_server_info(self):
        """从 API 获取服务器信息"""
        try:
            response = requests.get('http://kline.lianghuajia888.com:10030/api/QuoteServer/KlineServerList')
            if response.status_code == 200:
                res = response.json()
                if res['code'] == 200:
                    data = res['data'][0]
                    self.hsi_url = data['klineUrl']
                    return data['klineUrl']
                else:
                    pass
            else:
                logger.error(f"Failed to fetch server info: {response.status_code}")
        except Exception as e:
            logger.error(f"Failed to fetch server info: {traceback.format_exc()}")


    def update_hsi(self):
        """从服务器获取恒生指数数据并更新界面"""
        try:
            #print('self.hsi_url=',self.hsi_url)
            if self.hsi_url!='' and self.hsi_url is not None:
                # 构建请求 URL
                contract = f'HKEX%20HSI%{time.strftime("%Y%m", time.localtime())}'  # 2025年6月份的恒指
                url = f"{self.hsi_url}/api/Quote/LastQuoteData?contract={contract}&type=quote"
                #print(url)
                response = requests.get(url)
                if response.status_code == 200:
                    data = response.json()
                    #print(data)
                    hsi_value = data['data'][0]  # 假设返回的数据格式为 [{'lastPrice': 28000, ...}]
                    hsi_data=hsi_value.split(',')
                    hsi_name=f'{hsi_data[2]}{hsi_data[3]}'
                    hsi_value=hsi_data[12]
                    hsi_time=hsi_data[7]
                    #print(hsi_name,float(hsi_value))
                    return hsi_name,float(hsi_value)
                else:
                    return '',0
            else:
                return '',0
        except Exception as e:
            logger.error(f"Failed to fetch HSI data: {e}")
            return '',0

    
    def update_button_state(self, event=None):
        """根据选中项更新按钮状态"""
        selected_item = self.tree.focus()
        
        # 隐藏所有操作按钮
        self.start_btn.pack_forget()
        self.stop_btn.pack_forget()
        
        if not selected_item:
            self.status_var.set("就绪")
            return
        
        # 获取选中项的状态
        status = self.tree.item(selected_item, 'values')[6]  # 状态在第7列
        account_id = self.tree.item(selected_item, 'values')[1]
        
        # 更新状态栏
        self.status_var.set(f"当前选中: {self.tree.item(selected_item, 'values')[4]} {self.tree.item(selected_item, 'values')[5]} - 状态: {status}")
        
        # 根据状态显示相应按钮
        if status == "已停止":
            self.start_btn.pack(side=tk.LEFT, padx=5)
            self.start_btn['state'] = tk.NORMAL
        elif status == "已启动":
            self.stop_btn.pack(side=tk.LEFT, padx=5)
            self.stop_btn['state'] = tk.NORMAL
    
    def start_account(self):
        """启动选中账户"""
        selected_items = self.tree.selection()
        for item in selected_items:
            # 获取项的所有属性
            item_attributes = self.tree.item(item, "values")
        self.status_var.set(f"正在启动模拟器: {item_attributes[4]} {item_attributes[5]} ...")
        self.root.update()
        device_id=item_attributes[4]
        if item_attributes[6]=='已停止':
            p = Process(target= DevicePlayer(device_id,item_attributes[5],item_attributes[0]).connect_device, args=(),name=f'{device_id}')
            p.daemon=True  # 设为守护进程，主进程退出，子进程自动退出
            p.start()
            process_info_list.append({
                        'pid': p.pid,
                        'device_id': device_id,
                        'device_name': item_attributes[5],
                        'process': p

                    })
            
            db1=sqlite3.connect(r"db.sqlite3")
            cursor1=db1.cursor()
            cursor1.execute(f"update user set device_status='已启动' where device_id='{device_id}'")
            db1.commit()
            db1.close()
        time.sleep(2)
        
        self.tree.set(item_attributes[0],'status','已启动')
        

        
        self.status_var.set(f"已启动模拟器: {item_attributes[4]} {item_attributes[5]} ")
        self.update_button_state()
            
            
    
    def stop_account(self):
        """停止选中账户"""
        global process_info_list
        selected_items = self.tree.selection()
        
        for item in selected_items:
            # 获取项的所有属性
            item_attributes = self.tree.item(item, "values")
            #print(item_attributes,item_attributes[1])  # 打印选中项的信息
        if item_attributes[6]=='已启动':
            device_id=item_attributes[4]
            self.status_var.set(f"正在停止模拟器: {item_attributes[4]} {item_attributes[5]} ...")
            self.root.update()
            #print('停止进程，显示所有进程：',process_info_list)
            if item_attributes[6]=='已启动':
                for p in process_info_list:
                    #print(f"进程名：{p['device_id']}  进程id：{p['pid']}")
                    if p['device_id'] == device_id:
                        p['process'].terminate()
                        self.tree.set(item_attributes[0],'status','已停止')
                        process_info_list.remove(p)
                        db1=sqlite3.connect(r"db.sqlite3")
                        cursor1=db1.cursor()
                        cursor1.execute(f"update user set device_status='已停止' where device_id='{device_id}'")
                        db1.commit()
                        db1.close()
                        #print(f"{p['device_id']} 进程已终止！")
                        time.sleep(2)
                        
                
            self.status_var.set(f"已停止模拟器: {item_attributes[4]} {item_attributes[5]} ")
            self.update_button_state()
        
            
    
    def refresh_data(self):
        """刷新数据"""
        try:
            #self.status_var.set("正在刷新数据...")
            #self.root.update()
            db1=sqlite3.connect(r"db.sqlite3")
            cursor1=db1.cursor()
            cursor1.execute("select * from user")
            users=cursor1.fetchall()
            #print(f'执行表格数据更新！')
            #print(users)
            #print(tree_rowids)

            self.tree.tag_configure('red_text_tag', foreground='red')
            self.tree.tag_configure('green_text_tag', foreground='green')
            '''
            column_headers = {
            'row_num': '行编号',
            'account_id': '账号',
            'account_type': '类型',
            'name': '姓名',
            'simulator_id': '模拟器ID',
            'simulator_name': '模拟器名称',
            'status': '状态',
            'balance': '账户金额',
            'trade_index': '成交指数',
            'floating_pl': '浮动盈亏',
            'closed_pl': '平仓盈亏',
            'fee': '手续费',
            'round_times': '轮/次',
            'update_time': '更新时间'
            }
            '''
            for dr in users:
                #('行编号','账号','类型', '姓名','模拟器ID','模拟器名称','状态','账户金额','成交指数','浮动盈亏','平仓盈亏','手续费','轮/次','更新时间')
                self.tree.set(dr[0],'account_id',dr[1])
                self.tree.set(dr[0],'account_type',dr[2])
                self.tree.set(dr[0],'name',dr[3])
                self.tree.set(dr[0],'balance',dr[4])
                self.tree.set(dr[0],'floating_pl',f'+{dr[5]}' if dr[5] > 0  else dr[5])
                self.tree.set(dr[0],'closed_pl',f'+{dr[6]}' if dr[6] > 0  else dr[6])
                
                self.tree.set(dr[0],'fee',dr[7])
                self.tree.set(dr[0],'status',dr[11])
                self.tree.set(dr[0],'round_times',f'第{dr[14]}轮，第{dr[15]}次')
                now_time=datetime.datetime.now()
                edittime=datetime.datetime.strptime(dr[13], '%Y-%m-%d %H:%M:%S')
                delta = now_time - edittime
                # 获取相差的秒数
                seconds = delta.total_seconds()
                if seconds > 60:
                    self.tree.set(dr[0],'update_time',f'{int(seconds/60)}分前')
                else:
                    self.tree.set(dr[0],'update_time',f'{int(seconds)}秒前')

                if dr[5]>0:
                    self.tree.item(dr[0],tags=('red_text_tag',))
                elif dr[5]<0:
                    self.tree.item(dr[0],tags=('green_text_tag',))
                #print(f'{dr[0]}   平仓盈亏：{dr[7]}   浮动盈亏：{dr[6]}  --已更新')
                    
                
            db1.close()
            self.update_button_state()
        except:
            logger.error(traceback.format_exc())
        finally:
            db1.close()
            self.root.after(2000, self.refresh_data)

class DevicePlayer:
    def __init__(self, device_id,device_name,rowid):
        self.device_id = device_id          # 模拟器编号
        self.device_name = device_name      # 模拟器名称
        self.user_id = ''          # app登录账号
        self.user_name = ''        # app登录的用户姓名
        self.user_type=''     # 用户类型,模拟账户,资管账户
        self.device = None      #驱动实例
        self.now_position =  None  # 当前持仓
        self.next_position = None     # 下一持仓预计
        self.is_set_zhiying = False       # 是否已设置止盈止损
        self.contract_list = ['HKEX MHI 2506','HKEX HSI 2506']        # 合约列表
        self.strategy_list = [{'round': 1, 'future': '小恒指','direction': '', 'num': 1}, 
                              {'round': 2, 'future': '小恒指','direction': '',  'num': 2},
                            {'round': 3, 'future': '恒指', 'direction': '', 'num': 1},
                            {'round': 4, 'future': '恒指', 'direction': '', 'num': 2}, 
                            {'round': 5, 'future': '恒指','direction': '',  'num': 4},
                            {'round': 6, 'future': '恒指', 'direction': '', 'num': 8}]
        self.db_conn = None
        self.db_cursor = None
        self.rowid = rowid
    
    def say(self,msg):
        try:
            engine = pyttsx3.init()
            engine.setProperty('voice', 'com.microsoft.speech.platform.voice.SynthesizerVoice')
            engine.setProperty('rate', 130)
            engine.setProperty('volume', 1.0)
            engine.say(msg)
            engine.runAndWait()
            return True
        except:
            logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 语音播放发生错误!')
            return False
            #logger.error(traceback.format_exc())
    def quit(self):
        self.db_cursor.execute(f"update user set device_status='已停止' where row_id='{self.rowid}'")
        self.db_conn.commit()
        self.db_conn.close()
    def connect_device(self):
        """连接模拟器"""
        try:
            global rounds_count
            logname = self.device_id+'_'+self.device_name+ '_log.txt'
            logger.add(logname, encoding="utf-8")
            logger.info('准备连接设备 %s ...' % self.device_id)
            d = u2.connect(self.device_id)
            logger.info(f'连接设备 {self.device_id} ({self.device_name}) 成功!')
            self.device = d
            
            self.db_conn=sqlite3.connect(r"db.sqlite3")
            self.db_cursor=self.db_conn.cursor()

            time.sleep(random.uniform(2, 3))

            if not self.getUserInfo():
                self.quit()
                return False

            time.sleep(1)

            self.db_cursor.execute("SELECT * FROM hsiplus where user_id=? and trade_date=? ORDER BY id DESC limit 1;",(self.user_id,time.strftime("%Y-%m-%d", time.localtime())))
            row = self.db_cursor.fetchone()
            if row is not None:
                rounds_count=row[4]
            else:
                rounds_count=1

            self.start_device()     # 开始执行脚本
            self.quit()
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 连接已断开！')
            return True
        except Exception as e:
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 连接设备发生错误')
            logger.error(traceback.format_exc())
            self.quit()
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 连接已断开！')
            return False

    def get_appuserinfo(self):
        try:
            pass
        except Exception as e:

            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 发生错误')
            logger.error(traceback.format_exc())
            return False

    def playSound(self,filename):
        """播放声音，只能是wav格式"""
        try:
            winsound.PlaySound(filename,winsound.SND_FILENAME)
        except:
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 播放声音发生错误！')

    def test(self):
        print("test")
        # ---风控强平判断----
        elements = self.device(textContains="风控警告")   
        for element in elements:
            print(element.get_text())
        elements = self.device(textContains="定时强平")   
        for element in elements:
            print(element.get_text())
        #-------------------



        #----判断止盈止损提示框-----
        elements = self.device(textContains="[止损止盈]")   
        for element in elements:
            print(element.get_text())
        
        elements = self.device(textContains="触发止盈")    
        for element in elements:
            print(element.get_text())
        
        if self.device.xpath('//*[@resource-id="android:id/button1"]').exists:  #止损止盈和强平提示的关闭按钮都是这个
            print('有止盈止损提示框活着强平提示框，点击知道了')   
            elements = self.device(textContains="委托数量")   # 查找提示内容  关键字 委托数量 -》 “HKEX MHI 2505 触发止损,委托数量:1手,已平仓1手”
            for element in elements:
                print(element.get_text())   
                # 根据内容可判断盈亏，合约，手数，判断下一单策略，要是没有出现止损止盈提示，但是标记是下单了，持单现在为空，
                # 可以警告提醒人工处理，防止漏单
            self.device.xpath('//*[@resource-id="android:id/button1"]').click()  # 点击知道了 关闭提示框
        if self.device.xpath('//android.view.View[@text="确认"]').exists:  # 如果有确认按钮，点击确认
            self.device.xpath('//android.view.View[@text="确认"]').click() # 同时点击个确认，可能会存在一个信息提示框
        
        #------------------------
        # -----判断交易提示框-----
        if self.device.xpath('//*[@text="交易提示"]').exists:
            print(self.device.xpath('//android.view.View[@text="交易提示"]').get_text())
            print(self.device.xpath('//*[@text="确认"]').get_text())
            time.sleep(2)
            self.device.xpath('//android.view.View[@text="确认"]').click()

        #----------------------
        # -----判断最新平仓结果----
        # 比较麻烦，不好判断
        # ----------------------
        # -----判断持仓情况----
        print("判断持仓情况")
        '''
        self.device.drag(132, 787, 488, 787, 0.03)  # 向右拖动,显示最左边
        time.sleep(1)
        self.device.drag(132, 1280, 488, 787, 0.03)  # 向右拖动,显示最左边
        time.sleep(1)
        
        hymc=self.device.xpath('(//android.view.View)[55]').get_text()  #合约  （可能会有变动）
        direction=self.device.xpath('(//android.view.View)[57]').get_text() #方向   
        dt=self.device.xpath('(//android.view.View)[59]').get_text()  #动态盈亏
        cjjj=self.device.xpath('(//android.view.View)[58]').get_text() # 成交价
        time.sleep(1)
        self.device.drag(488, 780, 186, 780, 0.03)  # 向左拖动,显示最右边
        time.sleep(1)
        ccsl=self.device.xpath('(//android.view.View)[58]').get_text() # 持仓数量
        self.device.drag(488, 780, 186, 780, 0.03)  # 向左拖动,显示最右边
        time.sleep(1)
        jsjg=self.device.xpath('(//android.view.View)[62]').get_text() # 实时价格
        print(f'当前持仓为：{hymc} {direction} {dt} {cjjj} {ccsl} {jsjg}')
        time.sleep(random.uniform(5,10))
        '''
        # 列表的方式，索引是变化的，不一定准。。。

            

        #---------------------
        


        
        #----交易成功提示框内容
        if self.device.xpath('//android.view.View[@text="交易提示"]').exists:
            print(self.device.xpath('//android.widget.TextView[@index="0"]').get_text())
            print(self.device.xpath('//android.view.View[@text="交易提示"]').get_text())
            print(self.device.xpath('(//android.view.View)[102]').get_text())
            print(self.device.xpath('//android.view.View[@text="确认"]').get_text())
        #-----------------------
        #-----手动平仓
        #self.device.xpath('//android.view.View[@text="-"]').click()  # 点击平仓图标、

        time.sleep(1)

        #----委托交易提示框-----
        if self.device.xpath('//android.view.View[@text="委托提示"]').exists:
            print(self.device.xpath('//android.view.View[@text="委托提示"]').get_text())
            print(self.device.xpath('(//android.view.View)[99]').get_text())   #合约
            print(self.device.xpath('(//android.view.View)[100]').get_text())  #方向
            print(self.device.xpath('(//android.view.View)[101]').get_text())   #数量
            print(self.device.xpath('//android.view.View[@text="确认"]').get_text())
            print(self.device.xpath('//android.view.View[@text="取消"]').get_text())
            time.sleep(2)
            self.device.xpath('//android.view.View[@text="取消"]').click()
        #---------
        # -------判断持仓情况----
        if self.device.xpath('//android.view.View[@text="-"]').exists:
            print('有平仓图标，表示有持仓,点开止盈止损')
            self.device.drag(816, 1280, 100, 1280, 0.03)  # 拖动到屏幕中间,向左拖动
            time.sleep(1)
            self.device.drag(816, 1280, 100, 1280, 0.03)  # 拖动到屏幕中间，向左拖动
            time.sleep(1)
            self.device.xpath('//*[@text="查看"]').click()  # 点击查看按钮，打开止盈止损

            time.sleep(3)
            # 判断止盈止损页面是打开
            if self.device.xpath('//android.widget.TextView[@text="止盈止损(HKEX MHI 2505)"]').exists:
                print('止盈止损页面打开')
                if self.device.xpath('//*[@text="添加"]').exists:
                    self.device.xpath('//*[@text="添加"]').click()
                    #print('点击添加')
                    time.sleep(3)
                    if self.device.xpath('(//android.widget.TextView)[13]').exists:
                        self.device.xpath('(//android.widget.TextView)[13]').click()
                        #print('添加止盈止损')
                else:
                    print('没有添加按钮，表示已经有设置止盈止损')
                    if self.device.xpath('//*[@text="删除"]').exists:
                        #print('有删除止盈止损，表示已设置')
                        time.sleep(3)
                        self.device.click(43.9, 90.6)  # 点击左上角按钮，返回主页



        time.sleep(random.uniform(5, 20))
        print(f'{self.device_id} test执行完毕！！')
   
                

                

                        
        

    def disconnect_device(self):
        """断开与模拟器的连接"""
        global process_list
        print(f'断开设备 {self.device_id} ({self.user_name}) 进程...')
        print(f'进程列表：{process_list}')
        if self.device is not None:
            for p in process_list:
                print(f'进程名：{p.name}  进程id：{p.pid}')
                if p.name == self.device_id:
                    p.terminate()
                    process_list.remove(p)
                    logger.info(f'断开设备 {self.device_id} ({self.user_name}) 进程已终止！!')
                    logger.info(f'断开设备 {self.device_id} ({self.user_name}) 成功!') 
                    break
        else:
            print(self.device)


        

    def login(self):
        if self.device(text='登录交易').exists():
            
            time.sleep(0.5)
            self.device(resourceId="com.dalhwkhw.moni:id/username").set_text('S990008')
            self.device(resourceId="com.dalhwkhw.moni:id/psw").set_text('Icewxb2000')
            time.sleep(0.5)
            if not self.device(resourceId="com.dalhwkhw.moni:id/close_psw").info['checked']:
                self.device(resourceId="com.dalhwkhw.moni:id/close_psw").click()
            time.sleep(0.5)
            self.device(text='登录交易').click()
        else:
            logger.info(f'设备 {self.device_id} ({self.user_name}) 登录失败，不是登录页面!\n')


    def getStrategNext(self,last_position,yk='止损'):
        '''
        获取下一次策略信息
        yk:止损 or 止盈
        '''
        try:
            next_position={'round': 0, 'future': '','direction': '', 'num': 0}
            for item in self.strategy_list:
                if last_position['future']==item['future'] and last_position['num']==item['num']:
                    #找到了最后一次的平仓信息，要根据最后一次平仓信息来判断下一次持仓的策略
                    if yk=='止损':
                        if item['round']<6:
                            next_position=self.strategy_list[item['round']]  # 因为序号是从0开始的，所以下次策略不用+1
                            next_position['direction']=random.choice(['买','卖'])
                        else:
                            next_position=self.strategy_list[0]  # 如果已经是第六次了，就重新回到第一次
                            next_position['direction']=random.choice(['买','卖'])
                        logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}   最后平仓为：{last_position}  止损')
                        logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}   下一策略为：{next_position}')
                    elif yk=='止盈':
                        next_position=self.strategy_list[0]
                        next_position['direction']=random.choice(['买','卖'])
                        logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}   最后平仓为：{last_position}  止盈')
                        logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}   下一策略为：{next_position}')
                    return next_position    
            return None # 没有找到对应策略
        except Exception as e:
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 发生错误')
            logger.error(traceback.format_exc())
            return None

    def set_zhiying(self):
        """设置止盈止损"""
        _result=False
        try:
            self.device.drag(134, 766, 759, 766, 0.03)  # 向右拖动，显示最左边
            time.sleep(1)
            self.device.drag(134, 766, 759, 766, 0.03)  # 向右拖动，显示左边
            time.sleep(1)
            self.device.drag(492, 782, 55, 782, 0.03)  # 向左拖动，显示右边
            time.sleep(1)
            self.device.drag(474, 782, 317, 782, 0.03)  # 向左拖动，显示右边
            time.sleep(1)
            self.device.click(248, 801)
            time.sleep(1)
            if self.device.xpath('//*[@text="查看"]').exists:
                  self.device.xpath('//*[@text="查看"]').click()  # 点击查看按钮，打开止盈止损  （会查找失败！！）
            else:
                #logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 使用图片比对 查看')
                self.device.screenshot().save('full_%s.png' % self.device_id)
                #1 可能会出现全黑的截图，这个需要判断下截图大小，如果太小的话就说明有问题，需要重启下模拟器
                #2 页面数据也可能出现不刷新的情况，需要切换下页面尝试
                imsrc = ac.imread('full_%s.png' % self.device_id)  # 源图，全范围图
                imobj = ac.imread('chakan.png')  # 查看图标
                pos = ac.find_template(imsrc, imobj, 0.8)
                #{'result': (428.5, 600.0), 'rectangle': ((405, 587), (405, 613), (452, 587), (452, 613)), 'confidence': 1.0}
                x,y=pos['result']
                #print(x,y)
                logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) {pos}')
                if pos is None:
                    #logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 没有找到查看图片')
                    return False
                else:
                    self.device.click(x, y)  # 点击查看按钮，打开止盈止损

            time.sleep(3)
            elements = self.device(textContains="止盈止损执行可能")   
            if elements.exists:
                if self.device.xpath('//*[@text="添加"]').exists:
                    self.device.xpath('//*[@text="添加"]').click()
                    time.sleep(2)
                    self.device.click(261, 561)  # 再次点击弹出的添加确认按钮
                    time.sleep(2)
                    if self.device.xpath('//*[@text="删除"]').exists:
                        #有删除止盈止损，表示已设置
                        _result= True
                    else:
                        _result= False
                    
                    
                else:
                    #没有添加按钮，表示已经有设置止盈止损
                    if self.device.xpath('//*[@text="删除"]').exists:
                        #有删除止盈止损，表示已设置
                        _result= True
                
                time.sleep(2)
                self.device.click(30, 68)  # 点击左上角按钮，返回主页
                time.sleep(2)
                self.device.click(180, 773) #点击主页空白区域
                    
                
            else:
                _result= False

            time.sleep(2)
            self.device(text='持仓').click() # 点击持仓
            time.sleep(2)
            self.device.drag(134, 766, 759, 766, 0.03)  # 向右拖动，显示最左边
            time.sleep(1)
            self.device.drag(134, 766, 759, 766, 0.03)  # 向右拖动，显示左边
            time.sleep(1)
            return _result
        except:
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 设置止盈止损发生错误')
            logger.error(traceback.format_exc())
            return False
        

    def checkStrategy(self,last_position):
        """检查内容是否符合策略"""
        if last_position is None:
            return None
        for item in self.strategy_list:
            if last_position['future']==item['future'] and last_position['num']==item['num']:
                #找到匹配策略
                return item
        return None # 没有找到对应策略
    
    def close_other_prompts(self):
        """关闭其他无用弹窗"""
        try:
            if self.device.xpath('//*[@resource-id="android:id/alertTitle"]').exists:
                title=self.device.xpath('//*[@resource-id="android:id/alertTitle"]').get_text()
                if '通知'in title:  #通知类的不要，可以关闭
                    self.device.xpath('//*[@resource-id="android:id/button1"]').click()
            return True
        except: 
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 发生错误')
            logger.error(traceback.print_exc())
            return False

    def check_is_xunlian_page(self):
        """检查是否在训练页面"""
        try:
            pass  #待完善
        except:
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 发生错误')
            logger.error(traceback.format_exc())
            return False
    
    
    def select_hkex_type(self, future='小恒指'):
        """type: 恒指 or 小恒指
        """
        try:
            self.device.click(250, 217)
            if future == '恒指':
                time.sleep(1)
                self.device.click(149, 323)
                time.sleep(0.5)
            if future == '小恒指':
                time.sleep(1)
                self.device.click(150, 360)
                time.sleep(0.5)
            return True
        except:
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 发生错误')
            logger.error(traceback.format_exc())
            return False
        

    def setNumber(self,num):
        """设置手数"""
        try:
            self.device.click(201, 279)
            time.sleep(0.5)
            self.device.clear_text()
            time.sleep(1)
            self.device.send_keys(str(num))
            time.sleep(0.5)
            self.device.set_fastinput_ime(False)
            logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 设置手数为{num}')
            return True
        except:
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 发生错误')
            logger.error(traceback.format_exc())
            time.sleep(0.3)
            return False
    def getUserInfo(self):
        """获取当前登录客户信息"""
        self.device(text="我的").click()
        time.sleep(1)
        self.device.drag(311, 156, 311, 762, 0.1)
        time.sleep(1)
        self.device.drag(276, 794, 276, 240, 0.1)
        time.sleep(1)
        self.device.click(258, 848)

        time.sleep(1)
        self.device.drag(311, 156, 311, 762, 0.1)
        time.sleep(1)
        elements = self.device(textContains="@")    
        #for element in elements:
        #    print(element.get_text())
        self.user_id = elements[0].get_text()
        elements = self.device(textContains="模拟账号")    

        if len(elements) > 0:         
            self.user_type='模拟账号'
        else:
            self.user_type='资管账号'

        is_user_id_valid = self.check_userid_valid()
        logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 获取用户信息成功')
        
        if is_user_id_valid:
            logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 用户ID有效')
            # 将用户信息写入本地数据库
            self.db_cursor.execute("update user set user_id='%s',user_type='%s',user_name='%s' where device_id='%s';" % (self.user_id,self.user_type,self.user_name,self.device_id))
            self.db_conn.commit()

            time.sleep(1)
            self.device.click(32, 69)  # 点击返回
            time.sleep(2)
            self.device.click(336, 913)  # 点击返交易
            time.sleep(2)
            
        else:
            logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 当前用户无效，程序退出！！')
            time.sleep(1)
            self.device.click(274, 838)  # 点击返回

            
        return is_user_id_valid
        
     
    def check_userid_valid(self):
        """检查用户ID是否有效"""
        try:
            # 创建数据库连接
            connection = pymysql.connect(
                host='www.ey900.com',      # 数据库服务器地址
                user='root', # 用户名
                password='7f0860c2a29e619134ee30f8b5e45db55bba6b07', # 密码
                database='hisdb',  # 数据库名
                charset='utf8mb4',
                cursorclass=pymysql.cursors.DictCursor
            )
            
            cursor = connection.cursor()
            
            # 示例：执行SQL查询

            cursor.execute("SELECT * FROM userinfo WHERE userid='%s' and valid=1 and user_type='%s'" % (self.user_id,self.user_type))
            records = cursor.fetchone()
            #print(records)
            if records is not None:
                self.user_name = records['name']
                return True
            else:
                return False

        except:
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 发生错误')
            logger.error(traceback.format_exc())
            return False

        finally:
            connection.close()
            #print("数据库连接已关闭")   

    def pytesseract_pcyk(self):
        """解析平仓盈亏数字"""
        try:
            uiObjectBox = [197, 131, 381, 155]
            #tesseract_config = r'--oem 3 --psm 6 outputbase digits -'  # 只识别数字、小数点和-号
            self.device.screenshot().crop(uiObjectBox).save('pcyk_%s.png' % self.device_id)
            time.sleep(0.3)
            #content = pytesseract.image_to_string(d.screenshot().crop(uiObjectBox), config=tesseract_config)  # 解析图片
            #img = Image.open('pcyk_%s.png' % device_id)
            content = pytesseract.image_to_string(self.device.screenshot().crop(uiObjectBox), config=r'--oem 3 --psm 6 -l chi_sim')  # 识别中文
            content = content.replace('\n', '')  # 去掉换行符
            #print(f'{device_id}  :    平仓盈亏：{content}')
            content = content.split(':')  
            content = content[1]  # 去掉前缀文字
            #print(f'{device_id} :    平仓盈亏：{content}')
            return float(content)
        except:
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 平仓盈亏 {content}')

            return -1
    
    def pytesseract_total_amount(self):
        """解析权益金总金额数字"""
        try:
            uiObjectBox = [79, 109, 161, 128]
            #config=r'--oem 3 --psm 6 -l chi_sim'  # 识别中文,数字
            config = r'--oem 3 --psm 6 outputbase digits -'  # 只识别数字、小数点和-号
            self.device.screenshot().crop(uiObjectBox).save('total_amount_%s.png' % self.device_id)
            time.sleep(0.3)
            #content = pytesseract.image_to_string(d.screenshot().crop(uiObjectBox), config=tesseract_config)  # 解析图片
            #img = Image.open('pcyk_%s.png' % device_id)
            content = pytesseract.image_to_string(self.device.screenshot().crop(uiObjectBox), config=config)  # 识别中文
            content = content.replace('\n', '')  # 去掉换行符
            #print(f'{device_id}  :    账户金额：{content}')
            return float(content)
        except:
            return -1
        
    def pytesseract_commission_amount(self):
        """解析手续费总金额数字"""
        try:
            uiObjectBox = [388, 127, 535, 162]
            #tesseract_config = r'--oem 3 --psm 6 outputbase digits -'  # 只识别数字、小数点和-号
            self.device.screenshot().crop(uiObjectBox).save('commission_amount_%s.png' % self.device_id)
            time.sleep(0.3)
            #content = pytesseract.image_to_string(d.screenshot().crop(uiObjectBox), config=tesseract_config)  # 解析图片
            #img = Image.open('pcyk_%s.png' % device_id)
            content = pytesseract.image_to_string(self.device.screenshot().crop(uiObjectBox), config=r'--oem 3 --psm 6 -l chi_sim')  # 识别中文
            content = content.replace('\n', '')  # 去掉换行符
            #print(f'手续费：{content}')
            content = content.split(':')  
            content = content[1]  # 去掉前缀文字
            #print(f'{device_id} :    手续费：{content}')
            return float(content)
        except:
            #print(traceback.format_exc())
            return -1
    def pytesseract_fdyk(self):
        """解析训练实时动态数字(浮动盈亏)"""
        try:
            
            uiObjectBox = [108, 135, 170, 152]
            # uiObjectBox = [50, 110, 155, 131]
            tesseract_config = r'--oem 3 --psm 6 outputbase digits -'  # 只识别数字、小数点和-号
            self.device.screenshot().crop(uiObjectBox).save('fdyk_%s.png' % self.device_id)
            time.sleep(0.3)
            content = pytesseract.image_to_string(self.device.screenshot().crop(uiObjectBox), config=tesseract_config)  # 解析图片
            content = content.replace('\n', '')  # 去掉换行符
            # print(f'{device_id} :    浮动盈亏：{content}')
            return float(content)
        except:
            #print(traceback.format_exc())
            return -1
        
    def pytesseract_new(self):
        """解析最新行情指数"""
        try:
            '''判断是否在交易页面，根据是否有买入按钮来确定'''
            #d.screenshot().save('full_%s.png' % device_id)
            #imsrc = ac.imread('full_%s.png' % device_id)  # 源图，全范围图
            #imobj = ac.imread('mairu.png')  # 目标图，局部图
            #pos = ac.find_template(imsrc, imobj)
            #if pos is None:
            #    return 0
            # 开始截取指数图片进行识别
            uiObjectBox = [352, 184, 441, 222]  # 实时指数坐标位置
            tesseract_config = r'--oem 3 --psm 6 outputbase digits -'  # 只识别数字、小数点和-号
            # d.screenshot().crop(uiObjectBox).save('zhishu_%s.png' % device_id)
            content = pytesseract.image_to_string(self.device.screenshot().crop(uiObjectBox).convert('L'), config=tesseract_config)
            content = content.replace('\n', '')  # 去掉换行符
            zs= float(content)
            return zs
        except:
            return -1
        
    def pytesseract_check_is_chicang(self):
        """图片识别检查是否有持仓"""
        if self.device(text="持仓").exists():
            self.device(text="持仓").click()
            time.sleep(1)
            self.device.screenshot().save('full_%s.png' % self.device_id)
            #1 可能会出现全黑的截图，这个需要判断下截图大小，如果太小的话就说明有问题，需要重启下模拟器
            #2 页面数据也可能出现不刷新的情况，需要切换下页面尝试
            imsrc = ac.imread('full_%s.png' % self.device_id)  # 源图，全范围图
            imobj = ac.imread('pingcang_ico.png')  # 平仓图标，有就表示有持仓
            pos = ac.find_template(imsrc, imobj, 0.8)
            if pos is None:
                return False
            else:
                return True

    def pytesseract_ok(self):
        """交易成功弹窗提示"""
        try:
            uiObjectBox = [45, 305, 492, 568]
            self.device.screenshot().crop(uiObjectBox).save('msg_%s.png' % self.device_id)
            img = Image.open('msg_%s.png' % self.device_id)
            # content = pytesseract.image_to_string(d.screenshot().crop(uiObjectBox),lang='chi_sim')  # 识别中文
            custom_config = r'--oem 3 --psm 6 -l chi_sim'  # chi_sim for Simplified Chinese  # 尝试这个字库识别
            content = pytesseract.image_to_string(img, config=custom_config)  # 识别中文
            content = content.replace('\n', '')  # 去掉换行符
            #logger.warning(f'{self.device_id} :    ：{content}')
            return content
        except:
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 识别弹窗失败！')
            return None
    def pytesseract_pingcang(self):
        """手动平仓"""
        try:
            self.device.screenshot().save('full_%s.png' % self.device_id)
            imsrc = ac.imread('full_%s.png' % self.device_id)  # 源图，全范围图
            imobj = ac.imread('pingcang_ico.png')  # 目标图，局部图
            pos = ac.find_template(imsrc, imobj)
            #print(pos)
            if pos is None:
                logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 没有找到平仓图标,手动平仓失败')
                return False
            x, y = pos['result']
            self.device.click(x, y)
            while True:
                time.sleep(1)
                if self.device(text="确认").exists:
                    time.sleep(1)
                    msg = self.pytesseract_ok()
                    logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) {msg}')

                    if msg is not None:
                        if '委托提示' in msg:
                            self.device(text="确认").click()  # 委托成功提示
                            logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 手动平仓已委托成功')
                            break

            while True:
                time.sleep(1)
                if self.device(text="确认").exists:
                    time.sleep(1)
                    msg = self.pytesseract_ok()
                    logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) {msg}')
                    if msg is not None:
                        if '已成交' in msg:
                            self.device(text="确认").click()  # 委托成功提示
                            logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 平仓已执行')
                            break
            
            return True
        except:
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 手动平仓失败')
            logger.error(traceback.format_exc())
            return False


    def pytesseract_PlaceOrder(self, direction='买', future='小恒指', num=1):
        """下单
        direction:下单方向  买，卖
        future_type:期货类型  恒指，小恒指
        """
        try:
            # 首先确定是否在交易页面
            '''判断是否在交易页面，根据是否有买入按钮来确定'''
            time.sleep(2)
            self.device.screenshot().save('full_%s.png' % self.device_id)
            imsrc = ac.imread('full_%s.png' % self.device_id)  # 源图，全范围图
            imobj = ac.imread('mairu.png')  # 目标图，局部图
            pos = ac.find_template(imsrc, imobj, 0.8)
            #print('图标查找',pos)
            if pos is None:
                logger.error(f'{self.device_id}  当前不在交易页面，下单失败！')
                return False

            logger.info(f'准备下单 {direction} {future} {num}手...')
            self.select_hkex_type(future)
            self.setNumber(num)
            time.sleep(random.uniform(1, 2))
            if direction == '买':
                self.device(text="买入").click()
            if direction == '卖':
                self.device(text="卖出").click()
            time.sleep(random.uniform(1, 2))



            try:
                with open('lock.lock', 'w') as lock_file:
                    # 使用portalocker获取独占锁（阻塞直到获取成功）
                    portalocker.lock(lock_file, portalocker.LOCK_EX)
                    try:
                        # 临界区开始（同一时间只有一个进程能执行此处）
                        if self.device(text="确认").exists:
                            msg = self.pytesseract_ok()
                            #logger.warning(f'{msg}')
                            if msg is not None:
                                if '委托提示' in msg:
                                    self.device(text="确认").click()  # 委托成功提示
                                    logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 抢到锁，点击下单')
                                else:
                                    logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 抢到锁，但下单失败！') 
                                    return False
                        # 临界区结束
                    finally:
                        # 释放锁
                        time.sleep(4) # 等待5秒，再释放锁
                        portalocker.unlock(lock_file)
                        logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 释放锁')
                        
            except:
                logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 锁单发生错误')   
                logger.error(traceback.format_exc())
                return False


            while True:
                time.sleep(1)
                if self.device(text="确认").exists:
                    msg = self.pytesseract_ok()
                    #logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) {msg}')
                    if msg is not None:
                        if '已成交' in msg:
                            self.device(text="确认").click()  # 委托成功提示
                            logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 委托交易已确认成交！')
                            break
                        if '该品种已限制开仓' in msg  or '委托失败' in msg:
                            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 该品种已限制开仓')
                            self.device(text="确认").click()  # 关闭弹窗
                            time.sleep(random.randint(10, 30))
                            return False
                        if '尝试使用移动网络' in msg or '下单失败' in msg:
                            self.device(text="确认").click()  # 关闭弹窗
                            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 相同ip同时下单，暂停一下,稍后再试')
                            time.sleep(random.randint(5, 15))
                            return False
                        if '单笔下单量超过最大限定值' in msg:
                            self.device(text="确认").click()  # 关闭弹窗
                            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 单笔下单量超过最大限定值')
                            return False
            time.sleep(random.uniform(2, 3))
            return True
        except:
            logger.exception('设备 %s 执行发生错误！' % self.device_id)
            return False
    def pytesseract_lastpingcang(self):
        """文字识别最后平仓的策略"""
        try:
            global rounds_count
            self.device(text="成交").click()
            time.sleep(random.uniform(2, 5))
            #先将列表移动到最顶部
            self.device.drag(327, 586, 327, 840, 0.05)
            self.device.drag(327, 586, 327, 840, 0.05)
            self.device.drag(327, 586, 327, 840, 0.05)

            self.device.drag(135, 746, 408, 748, 0.05)
            self.device.drag(135, 746, 408, 748, 0.05)
            self.device.drag(135, 746, 408, 748, 0.05)
            time.sleep(1)
            uiObjectBox = [21, 585, 534, 626]
            #tesseract_config = r'--oem 3 --psm 6 outputbase digits -'  # 只识别数字、小数点和-号
            self.device.screenshot().crop(uiObjectBox).save('lastorder_%s.png' % self.device_id)
            time.sleep(0.3)
            #content = pytesseract.image_to_string(self.device.screenshot().crop(uiObjectBox), config=tesseract_config)  # 解析图片
            img = Image.open('lastorder_%s.png' % self.device_id)
            custom_config = r'--oem 3 --psm 6 -l chi_sim+eng -c tessedit_char_whitelist=HMIS.0123456789'  # chi_sim for Simplified Chinese  # 尝试这个字库识别
            content = pytesseract.image_to_string(img, config=custom_config)  # chi_sim for Simplified Chinese 
            content = content.replace('\n', '')  # 去掉换行符
            content = content.split(' ')
            content = [x for x in content if x]  # 去掉空字符串
            logger.info(f'{self.device_id} :    最新成交数据识别：{content}')
            if '没有' in content or len(content)==0:
                logger.info(f'{self.device_id} :    没有最新成交数据！')
                return None,None

            # 重新识别一次成交手数
            uiObjectBox = [296, 585, 405, 626]
            tesseract_config = r'--oem 3 --psm 6 outputbase digits -'  # 只识别数字、小数点和-号
            self.device.screenshot().crop(uiObjectBox).save('lastorder_0%s.png' % self.device_id)
            time.sleep(0.3)
            shoushu = pytesseract.image_to_string(self.device.screenshot().crop(uiObjectBox), config=tesseract_config)  # 解析图片
            shoushu = shoushu.replace('\n', '')  # 去掉换行符
            logger.info(f'{self.device_id} :    最后成交手数为：{shoushu}')

            # 然后识别平仓中的最后一手数据，如果盈利为负值，就是亏了，否则就是盈了
            self.device.click(462,465)
            time.sleep(random.uniform(2, 5))
            #滑动
            self.device.drag(205,700,435,700,0.05)
            self.device.drag(205,700,435,700,0.05)
            time.sleep(1)
            self.device.drag(495,700,180,700,0.05)
            time.sleep(1)
            
            #识别平仓最后一手的盈亏
            uiObjectBox = [380, 582, 490, 626]
            self.device.screenshot().crop(uiObjectBox).save('lastorder_1%s.png' % self.device_id)
            time.sleep(0.3)
            img = Image.open('lastorder_1%s.png' % self.device_id)
            tesseract_config = r'--oem 3 --psm 6 outputbase digits -'  # 只识别数字、小数点和-号
            yk = pytesseract.image_to_string(img, config=tesseract_config)  # 识别数字
            yk = yk.replace('\n', '')  # 去掉换行符
            yk = yk.replace(' ', '')
            #logger.info(f'{self.device_id} :    最新成交盈亏为：{yk}')
            if float(yk)<0:
                #logger.info(f'{self.device_id} :    最新成交盈亏为负值，亏损！')
                zyzs='止损'
            else:
                #logger.info(f'{self.device_id} :    最新成交盈亏为正值，盈利！')
                zyzs='止盈'
                

            # 识别平仓最后一手的数据
            #print(f'{content[0]},{content[2]}')
            if 'MHI' in content[0] :
                future='小恒指'
            else:
                future='恒指'

            
            _last_position = {'round':0,'direction': '', 'future': future, 'num': int(shoushu)}  
            now_round=0
            last_position=None
            for item in self.strategy_list:
                if _last_position['future']==item['future'] and _last_position['num']==item['num']:
                    now_round=item['round']
                    last_position=item
            logger.warning(f"{self.device_id}   当前最后一次平仓为：{last_position}, {zyzs} ,第{last_position['round']}次")
            
            time.sleep(1)
            self.device(text="持仓").click()
            
            return last_position,zyzs
        except:
            time.sleep(1)
            self.device(text="持仓").click()
            logger.exception('设备 %s 执行最后一手识别发生错误' % self.device_id)
            return None,None
    def pytesseract_chicang_info(self):
        '''文字识别解析当前持仓信息'''
        try:
            # 识别判断当前持仓的是什么
            self.device(text="持仓").click()
            time.sleep(1)
            # 执行3次滑动，回归初始位置
            self.device.drag(135, 746, 408, 748, 0.05)
            self.device.drag(135, 746, 408, 748, 0.05)
            self.device.drag(135, 746, 408, 748, 0.05)
            time.sleep(1)
            uiObjectBox = [91,583,303,625]
            self.device.screenshot().crop(uiObjectBox).save('chicang0_%s.png' % self.device_id)  #识别合约类型
            img = Image.open('chicang0_%s.png' % self.device_id)
            # content = pytesseract.image_to_string(self.device.screenshot().crop(uiObjectBox),lang='chi_sim')  # 识别中文
            content = pytesseract.image_to_string(img, lang='chi_sim')  # 识别中文
            content = content.replace('\n', '')  # 去掉换行符
            future = content.replace(' ', '')  # 去掉空格
            direction=''
            if '卖' in future:
                direction='卖'
            if '买' in future:
                direction='买'
            
            if 'MHI' in future:
                future='小恒指'
            if 'HSI' in future:
                future='恒指'
            #logger.warning(f'{device_id} :   合约 ：{future}  方向：{direction}')
            
            self.device.drag(496, 732, 245, 732, 0.05)

            uiObjectBox = [234,583,394,625]
            self.device.screenshot().crop(uiObjectBox).save('chicang2_%s.png' % self.device_id)  #识别成交均价
            img = Image.open('chicang2_%s.png' % self.device_id)
            # content = pytesseract.image_to_string(self.device.screenshot().crop(uiObjectBox),lang='chi_sim')  # 识别中文
            tesseract_config = r'--oem 3 --psm 6 outputbase'  # 只识别数字、小数点和-号
            content = pytesseract.image_to_string(img,config=tesseract_config)  # 识别数字
            cjjj = content.replace('\n', '')  # 去掉换行符
            cjjj=float(cjjj)
            logger.warning(f'{self.device_id} :   成交均价 ：{cjjj}')
            uiObjectBox = [398,583,469,625]
            self.device.screenshot().crop(uiObjectBox).save('chicang3_%s.png' % self.device_id)  #识别持仓数量
            img = Image.open('chicang3_%s.png' % self.device_id)
            tesseract_config = r'--oem 3 --psm 6 outputbase'  # 只识别数字、小数点和-号
            content = pytesseract.image_to_string(img,config=tesseract_config)  # 识别数字
            ccsl = content.replace('\n', '')  # 去掉换行符
            #logger.warning(f'{self.device_id} :   持仓数量 ：{ccsl}')
            
            _now_position = None 
            for item in self.strategy_list:
                if future==item['future'] and int(ccsl)==item['num']:
                    _now_position=item  #找到匹配的持仓策略
                    _now_position['direction']=direction
                    break

            self.device.drag(109, 721, 335, 721, 0.05)
            self.device.click(223, 769)  # 点击一下空白区域，清除底色
           
            return _now_position  #如果为空，表示持仓策略匹配失败
        except:
            logger.exception('设备 %s 执行发生错误！' % self.device_id)
            return None
    

    def start_device(self):
        """开始运行模拟器"""
        try:
            global rounds_count
            logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 开始运行')
            is_open_market=False  #初始将开市设置为False
            #没有持仓的话，先看看最后一条数是否是定时强平的，如果是，则按强平时的策略继续
            
            #1、获取app用户信息
            #2、判断是否在交易页面（训练页面）
            #3、检查持仓情况
            #4、检查挂单情况
            if not self.close_other_prompts():
                return False
            
            if self.device.xpath('//*[@text="训练"]').exists:
                pass
            else:
                logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 不在训练页面,或者有弹窗,程序已退出！')
                return False


            # 获取账户资金信息
            zje=self.pytesseract_total_amount()
            pcyk=self.pytesseract_pcyk()
            sxf=self.pytesseract_commission_amount()
            fdyk=self.pytesseract_fdyk()
            logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 账户资金信息：总额：{zje}' )

            self.db_cursor.execute("update user set user_total_amount=?,user_commission=?,user_now_amount=?,user_floating=?,edittime=?,rounds_count=? where device_id=?;",(zje,sxf,pcyk,fdyk,time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),rounds_count,self.device_id))
            self.db_conn.commit()

            if self.device(text='持仓').exists:
                self.device(text='持仓').click() # 点击持仓
            else:
                logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 查找持仓失败！')
                return False

            time.sleep(2)
            self.device.drag(134, 766, 759, 766, 0.03)  # 向右拖动，显示最左边
            time.sleep(1)
            self.device.drag(134, 766, 759, 766, 0.03)  # 向右拖动，显示左边
            time.sleep(1)

            starttime=datetime.datetime.strptime(str(datetime.datetime.now().date())+' 16:15:00', "%Y-%m-%d %H:%M:%S")
            endtime=datetime.datetime.strptime(str(datetime.datetime.now().date())+' 17:15:00', "%Y-%m-%d %H:%M:%S")
            starttime1=datetime.datetime.strptime(str(datetime.datetime.now().date())+' 02:40:00', "%Y-%m-%d %H:%M:%S")
            endtime1=datetime.datetime.strptime(str(datetime.datetime.now().date())+' 09:15:00', "%Y-%m-%d %H:%M:%S")
            starttime2=datetime.datetime.strptime(str(datetime.datetime.now().date())+' 12:00:00', "%Y-%m-%d %H:%M:%S")
            endtime2=datetime.datetime.strptime(str(datetime.datetime.now().date())+' 13:00:00', "%Y-%m-%d %H:%M:%S")

            # 判断是否开市
            if starttime<=datetime.datetime.now() and datetime.datetime.now()<=endtime or starttime1<=datetime.datetime.now() and datetime.datetime.now()<=endtime1 or starttime2<=datetime.datetime.now() and datetime.datetime.now()<=endtime2:
                is_open_market=False # 休市期间
                #print(f'{device[0]}  {device_id}  {time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}  当前已休市，开市后继续...[09:15-12:00,13:00-16:30,17:15-03:00]')
            else:
                is_open_market=True # 开市


            #判断是否有持仓
            run_count=1
            while True: # 如果有持仓，则循环监控
                time.sleep(1)
                if self.device.xpath('//android.view.View[@text="-"]').exists or self.pytesseract_check_is_chicang():
                    fdyk=self.pytesseract_fdyk() # 浮动盈亏保存
                    self.db_cursor.execute("update user set user_floating=?,edittime=? where device_id=?;",(fdyk,time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),self.device_id))
                    self.db_conn.commit()
                    if self.now_position is None:
                        _now_position=self.pytesseract_chicang_info()
                        if _now_position is not None:
                            print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {self.device_name} {self.device_id} ({self.user_id}，{self.user_name})  当前持仓 {_now_position}  正在监控...')
                            self.db_cursor.execute("SELECT * FROM hsiplus where user_id=? and trade_date=? ORDER BY id DESC limit 1;",(self.user_id,time.strftime("%Y-%m-%d", time.localtime())))
                            row = self.db_cursor.fetchone()
                            
                            if _now_position['round']==1 or _now_position['round']>=row[5]:
                                if rounds_count>1:
                                    rounds_count=rounds_count+1
                                else:
                                    rounds_count=1  #X修正第一次执行的轮次
                            
                            self.db_cursor.execute("update user set rounds_count=?,round=? where device_id=?;",(rounds_count,_now_position['round'],self.device_id))
                            self.db_conn.commit()
                            self.now_position=_now_position #    设置当前持仓标记
                            if not self.is_set_zhiying: # 没有设置止盈止损，则进行设置或查看止盈止损
                                self.is_set_zhiying=self.set_zhiying()
                            continue
                        else:
                            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 持仓检查失败，当前持仓没有匹配到策略！！')
                    else: # 当前持仓标记不为空
                        #fdyk=self.pytesseract_fdyk()
                        #new_zs=self.pytesseract_new()
                        print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {self.device_name} {self.device_id} ({self.user_id}，{self.user_name})  当前持仓 {self.now_position}  正在监控...')
                        #检查一下止盈止损
                        if run_count % 20 == 0:
                            self.is_set_zhiying=self.set_zhiying()
                            
                        time.sleep(2)  # 持仓标记不为空，并且有持仓，就持续监控
                    
                else:
                    logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 没有持仓')
                    break
            
            time.sleep(2)
            # 没有持仓，则检查是否有止损止盈弹出
            #----判断止盈止损提示框-----
            if self.now_position is not None:
                logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 持仓监控中...')
            zyzs=None
            while True :
                if self.now_position is not None:  # 有当前持仓标记，则死等止盈止损的提示框
                    print(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 有持仓标记，等待止盈止损的提示框...')
                    elements = self.device(textContains="[止损止盈]")   
                    if elements.count > 0:
                        self.playSound('msg.wav')
                        title=elements[0].get_text()
                        elements = self.device(textContains="委托数量")   
                        if elements.count > 0:
                            msg=elements[0].get_text()
                            logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name})  {title}  | {msg}')
                            yk=0
                            if "触发止盈" in msg:
                                yk=1
                                hymc,wtsl,pcsl= extract_zhiyingzhisun_info(msg)
                                logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name})  合约:{hymc}  触发止盈  委托数量:{wtsl} 平仓数量:{pcsl}')
                                
                            if "触发止损" in msg:
                                yk=-1
                                hymc,wtsl,pcsl= extract_zhiyingzhisun_info(msg)
                                logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name})  合约:{hymc}  触发止损  委托数量:{wtsl} 平仓数量:{pcsl}')
                            self.device.xpath('//*[@resource-id="android:id/button1"]').click()  # 点击知道了 关闭提示框
                            time.sleep(1)
                            if self.device.xpath('//android.view.View[@text="确认"]').exists:  # 如果有确认按钮，点击确认
                                logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name})  有确认按钮，点击确认')
                                self.device.xpath('//android.view.View[@text="确认"]').click() # 同时点击个确认，可能会存在一个信息提示框
                            else:
                                logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 没有确认按钮')

                            # 判断这个自动平仓的策略是第几单的
                            if 'MHI' in hymc:
                                future='小恒指'
                            if 'HSI' in hymc:
                                future='恒指'
                            num=int(pcsl)
                            if yk==1:
                                zyzs='止盈'
                            elif yk==-1:
                                zyzs='止损'

                            now_position = {'round': 0, 'future': future,'direction': '', 'num': num}  
                            _now_postion=self.checkStrategy(now_position)  # 检查策略
                            if _now_postion is not None:
                                #平仓策略有效
                                #将平仓结果存入数据库
                                self.db_cursor.execute(
                                "INSERT INTO hsiplus(device_id, user_id, trade_date, rounds_count, round,future,direction,quantity,phase,addtime,total_amount,now_amount,commission_amount) VALUES (?, ?, ?, ?, ?,?, ?, ?, ?, ?,?,?,?)",
                                (self.device_id, self.user_id, time.strftime("%Y-%m-%d", time.localtime()), rounds_count, _now_postion['round'], now_position["future"], _now_postion["direction"], _now_postion["num"], zyzs,
                                 time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),0,0,0))
                                self.db_conn.commit()
                                
                                if _now_postion['round']==6:
                                    if yk==1:
                                        logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 本轮已执行到第六次，止盈')
                                        rounds_count=rounds_count+1
                                        self.next_position=self.getStrategNext(_now_postion,zyzs)
                                        break
                                    else:
                                        logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 本轮已执行到第六次，止损,需要上报处理！程序停止运行！！')
                                        self.say(f'模拟器名称：{self.device_name} 姓名:{self.user_name}) 本轮已执行到第六次，已止损,需要上报处理！程序停止运行！！')
                                        return False
                                else:
                                    #不是第六次，则继续执行
                                    if zyzs=='止盈':
                                        rounds_count=rounds_count+1
                                    self.next_position=self.getStrategNext(_now_postion,zyzs)
                                    break
                            else:
                                logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 平仓检查失败，没有匹配到平仓策略')
                                self.say(f'模拟器名称：{self.device_name} 姓名:{self.user_name}) 平仓检查失败，没有匹配到平仓策略')
                                return False
                    time.sleep(random.uniform(5,10))
                    elements = self.device(textContains="定时强平")   
                    if elements.count > 0:
                        logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name})  定时强平')
                        self.device.xpath('//*[@resource-id="android:id/button1"]').click()  # 点击知道了 关闭提示框
                        time.sleep(1)
                        if self.device.xpath('//android.view.View[@text="确认"]').exists:  # 如果有确认按钮，点击确认
                            self.device.xpath('//android.view.View[@text="确认"]').click() # 同时点击个确认，可能会存在一个信息提示框
                           
                        # 数据存入数据库
                        self.db_cursor.execute(
                                "INSERT INTO hsiplus(device_id, user_id, trade_date, rounds_count, round,future,direction,quantity,phase,addtime,total_amount,now_amount,commission_amount) VALUES (?, ?, ?, ?, ?,?, ?, ?, ?, ?,?,?,?)",
                                (self.device_id, self.user_id, time.strftime("%Y-%m-%d", time.localtime()), rounds_count, self.now_postion['round'], self.now_position["future"], self.now_postion["direction"], self.now_postion["num"], '定时强平',
                                 time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),0,0,0))
                        self.db_conn.commit()
                        # 设置下一次策略为强平策略
                        self.next_position=self.now_position
                        self.now_position['direction']=random.choice(['买','卖'])  #重新指定方向
                        # 清空标记
                        self.now_position=None
                        zyzs=None
                        time.sleep(3)

                    
                else: #没有持仓标记，则退出循环
                    break
        
            # 判断当前持仓和下一手策略是否存在
            #初次执行，判断最后一次平仓数据，确定下一次策略
            if self.now_position is None and zyzs is not None:
                logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 判断最后一次平仓数据')
                _last_pingcang,zyzs=self.pytesseract_lastpingcang()
                if _last_pingcang is not None:
                    if _last_pingcang['round']==6 and zyzs=='止损':
                        logger.warning(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 本轮已执行到第六次，止损,需要上报处理！程序停止运行！！')
                        self.say(f'模拟器名称：{self.device_name} 姓名:{self.user_name}) 本轮已执行到第六次，已止损,需要上报处理！程序停止运行！！')
                        return False
                    else:
                        self.next_position=self.getStrategNext(_last_pingcang,zyzs)
                else:
                    logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 本日第一次运行，请先手动确认第一单！')
                    self.say(f'模拟器名称：{self.device_name}) 本日第一次运行，请先手动确认第一单')
                    return False
            
            while True:
                if check_Istransaction():
                    if self.next_position is not None:
                        logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 下一次策略 {self.next_position}')
                        # 准备下单
                        isXD=self.pytesseract_PlaceOrder(self.next_position['direction'],self.next_position['future'],self.next_position['num'])
                        if isXD:
                            logger.info(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 下单成功')
                            self.db_cursor.execute("update user set rounds_count=?,round=? where device_id=?;",(rounds_count,self.next_position['round'],self.device_id))
                            self.db_conn.commit()
                            self.is_set_zhiying = self.set_zhiying()  # 设置止盈止损
                            self.now_position=self.next_position
                            self.next_position=None
                            self.start_device()
                        else:
                            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 下单失败')
                            self.now_position=None
                            self.next_position=None
                            self.start_device()
                    else:
                        logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 下一次策略为空,设置失败')
                        self.say(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 下一次策略为空,设置失败，程序退出！')
                        return False
                else:
                    print(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 当前时间段不允许下单,等待中...[09:30~12:00][13:00~15:30][17:15~22:00]')
                    time.sleep(random.uniform(5,10))



        except Exception as e:
            logger.error(f'{self.device_name} {self.device_id} ({self.user_id}，{self.user_name}) 发生错误')
            logger.error(traceback.format_exc())
            return False

        return
    

def extract_zhiyingzhisun_info(trade_str):
    """ 提取止盈止损关键信息
    HKEX MHI 2506 触发止盈,委托数量:1手,已平仓1手
    1. "委托数量:X手"中的数字
    2. "已平仓X手"中的数字
    3. "触发止盈"之前的字符串
    
    参数:
        trade_str (str): 交易信息字符串
        
    返回:
        tuple: (委托数量, 已平仓数量, 合约信息)
    """
    # 提取委托数量
    order_qty_match = re.search(r'委托数量:(\d+)手', trade_str)
    wtsl = int(order_qty_match.group(1)) if order_qty_match else None
    
    # 提取已平仓数量
    closed_qty_match = re.search(r'已平仓(\d+)手', trade_str)
    pcsl = int(closed_qty_match.group(1)) if closed_qty_match else None
    
    # 提取"触发止盈"之前的字符串
    contract_match = re.search(r'(.+?)触发止盈', trade_str)
    hymc = contract_match.group(1).strip() if contract_match else None

    if hymc is None:
        contract_match = re.search(r'(.+?)触发止损', trade_str)
        hymc = contract_match.group(1).strip() if contract_match else None

    return hymc, wtsl, pcsl
    
def remove_digits(s):
    # 移除字符串中的数字
    return re.sub(r'\d', '', s)
def check_Istransaction():
    """判断当前时间是否允许下单"""
    time_format = '%H:%M:%S'
    today = datetime.datetime.now()
    #print(today)
    weekday = today.weekday()
    if weekday == 5 or weekday == 6:
        return False

    # 定义开始时间和结束时间
    start_time = datetime.datetime.strptime(str(datetime.datetime.now().date())+' 09:30:10', "%Y-%m-%d %H:%M:%S")
    end_time = datetime.datetime.strptime(str(datetime.datetime.now().date())+' 12:00:00', "%Y-%m-%d %H:%M:%S")
    if today>= start_time and today <= end_time:
        return True
    start_time = datetime.datetime.strptime(str(datetime.datetime.now().date())+' 13:00:10', "%Y-%m-%d %H:%M:%S")
    end_time = datetime.datetime.strptime(str(datetime.datetime.now().date())+' 15:30:00', "%Y-%m-%d %H:%M:%S")
    if today>= start_time and today <= end_time:
        return True
    start_time = datetime.datetime.strptime(str(datetime.datetime.now().date())+' 17:15:10', "%Y-%m-%d %H:%M:%S")
    end_time = datetime.datetime.strptime(str(datetime.datetime.now().date())+' 22:00:00', "%Y-%m-%d %H:%M:%S")
    if today>= start_time and today <= end_time:
        return True
    
    return False


def process_check():
    """监控进程状态"""
    import psutil
    try:
        print('启动进程监控!!')
        global process_info_list
        while True:
            print('进程监控：',process_info_list)
            for info in list(process_info_list):
                try:
                    process = psutil.Process(info['pid'])
                    if not process.is_running():
                        process_info_list.remove(info)
                        print(f'{info["device_name"]} {info["device_id"]} 进程已停止')

                except:  # 
                    process_info_list.remove(info)
                    print(f'{info["device_name"]} {info["device_id"]} 进程已停止')
            time.sleep(5)
        
    except:
        print('程序异常')
        print(traceback.format_exc())

    


                    
def main():
    root = tk.Tk()
    # 设置窗口主题
    style = ttk.Style()
    style.theme_use('clam')
    app = TradingManagerApp(root)
    x = (root.winfo_screenwidth() - 1320) // 2
    y = (root.winfo_screenheight() - 600) // 2
    # 设置窗口大小和位置
    root.geometry(f"{1320}x{600}+{x}+{y}")
    # 禁止调整窗口大小（宽度不可调，高度可调）
    root.resizable(False, False)
    root.mainloop()



if __name__ == '__main__':
    # Windows平台多进程需要freeze_support
    freeze_support()
    main()
    
   
